package controller;

import game.*;
import java.awt.*;
import java.io.File;
import java.lang.reflect.*;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import view.*;

/**
 * <p>The controller for a game of chess.</p>
 * 
 * @author Nicole Waldrum
 * @date January 30, 2010
 * @version 1.0
 * 
 * @author Trevor Bastian
 * @date February 22, 2010
 * @version 2.0
 * @version 4.0
 * 
 * @author Mark Kelly
 * @date February 24, 2010
 * @version 2.0
 * 
 * @author Mark Kelly
 * @date March 8, 2010
 * @version 3.0
 *
 */
public class ChessController implements xml.XMLizeable {
	private GameState gameState;
	private Point previousPoint;
	private Main main;
	
	public void setMain(Main main) {
		this.main = main;
	}
	
	/**
	 * <p>Creates a new instance of <tt>ChessController</tt></p>
	 * @param cv a <tt>ChessView</tt>
	 */
	public ChessController() {
		previousPoint = null;
		gameState = new GameState();
	}

	private ChessController(Element e) {
		previousPoint = null;
		this.gameState = GameState.importFromXML(e);
	}
	
	/**
	 * <p>Returns a {@code String} representation of the {@code ChessBoard}</p>
	 * 
	 * @return a {@code String} representation of the {@code ChessBoard}
	 */
	public String boardString() {
		return gameState.getBoard().toString();
	}
 
	public void boardButtonClicked(BoardButton bb) {
		movePiece(bb);
	}
	
	/**
	 * <p>Always sends the current click and the prior click.</p>
	 * 
	 * @param bb the {@code BoardButton} that was pressed
	 */
	private void movePiece(BoardButton bb) {
		gameState.getGameType().takeTurn(previousPoint, bb.getLocation());
		previousPoint = bb.getLocation();
	}
	
	/**
	 * <p>Quit the game</p>
	 */
	public void quit() {
		System.exit(0);
	}

	/**
	 * <p>Starts a game of the specified type.</p>
	 * 
	 * @param <T> they type of game
	 * @param clazz a {@code Class<T>} representing the type of game
	 */
	public <T extends GameType> void startGame(Class<T> clazz, ChessView... view) {
		Constructor<T> constructor = null;
		GameType g = null;
		
		try {
			constructor = clazz.getConstructor(); // retrieve the constructor
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
		
		try {
			g = constructor.newInstance(); // invoke the constructor
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		
		gameState = new GameState(g);
		g.setGameState(gameState);
		gameState.addGameStateListener(main.unre);
		
		for (ChessView cv : view) {
			gameState.addGameStateListener(cv);
			gameState.addOutcomeListener(cv);
		}
		
		gameState.notifyGameStarted();
	}

	/**
	 * Returns the gameState
	 * @return
	 */
	public GameState getGameState () {
		return gameState;
	}
	
	/**
	 * Imports a ChessController from an XML element
	 * @param elem
	 * @return a new ChessController
	 */
	public static ChessController importFromXML(Element elem) {
		return new ChessController((Element)elem.getElementsByTagName(GameState.class.getName()).item(0));
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Element toXML(Document doc) {
		Element controller = doc.createElement(ChessController.class.getName());
		controller.appendChild(gameState.toXML(doc));
		return controller;
	}
	
	/**
	 * Saves the game to 'chess.xml'
	 */
	public void save() {
		xml.XMLHandler.saveToFile(new File("chess.xml"), gameState, "Chess");
	}
	
	/**
	 * looks for and loads 'chess.xml'
	 * @param view
	 */
	public void load(view.ChessGUI view) {
		gameState = xml.XMLHandler.loadGameStateFromFile(new File("chess.xml"));

		gameState.addGameStateListener(view);
		gameState.addOutcomeListener(view);
	}
	
	/**
	 * Loads the game from a {@code Document} object
	 * @param doc the document to load
	 * @param view the veiw which will be displaying said saved game
	 */
	public void load(Document doc, view.ChessGUI view) {
		gameState = xml.XMLHandler.loadGameStateFromDoc(doc);

		gameState.addGameStateListener(view);
		gameState.addOutcomeListener(view);
	}
	
	/**
	 * pops the latest gamestate off the undo stack and loads it
	 * @param view
	 */
	public void undo(view.ChessGUI view) {
		Document temp = main.unre.undo();
		if (temp != null)
			load(temp, view);
		else
			System.out.println("null undo");
	}
	
	/**
	 * pops the latest gamestate off the redo stack and loads it
	 * @param view
	 */
	public void redo(view.ChessGUI view) {
		Document temp = main.unre.redo();
		if (temp != null)
			load(temp, view);
		else
			System.out.println("null redo");
	}
}
